Inversify Props
This package is a wrapper of Inversify to simplify how inject your dependencies with property decorators in the components, made with TypeScript and compatible with Vue, React and other component libraries.
Do you use Hooks? You can try the experimental package inversify-hooks
Installation
$ npm install inversify-props reflect-metadata --save
The inversify-props type definitions are included in the inversify-props npm package.
How to use
import 'reflect-metadata';
import { container, inject } from 'inversify-props';
container.addSingleton<IService1>(Service1);
container.addSingleton<IService2>(Service2);
export default class extends Component {
@inject() service1: IService1;
@inject() service2: IService2;
}
Alternative usage (without magic)
import 'reflect-metadata';
import { cid, container, inject } from 'inversify-props';
container.addSingleton<IService1>(Service1);
export default class extends Component {
@inject(cid.IService1) service1: IService1;
}
How to use this library outside of a component
container.addSingleton<IService1>(Service1, 'MyService1');
export class MyOtherService {
@inject() private service1: IService1;
}
export class MyOtherService {
constructor(@inject() private exampleService: IExampleService) {}
}
export function myHelper() {
const service1 = container.get<IService1>(cid.IService1);
}
You can also use any ID that you prefer
import 'reflect-metadata';
import { container, inject } from 'inversify-props';
container.addSingleton<IService1>(Service1, 'MyService1');
export default class extends Component {
@inject('MyService1') service1: IService1;
}
:warning: Important! inversify-props requires TypeScript >= 2.0 and the experimentalDecorators
, emitDecoratorMetadata
, types
and lib
compilation options in your tsconfig.json
file.
{
"compilerOptions": {
"target": "es5",
"lib": ["es6"],
"types": ["reflect-metadata"],
"module": "commonjs",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Why we made this package
The idea is to add a simple wrapper that helps us to inject dependencies in components using property decorators
, we have also extend a little inversify
adding some methods that make our experience injecting dependencies easier.
You probably don't need this if:
- You have experience using inversify and you don't need to simplify the process.
- You want to use all the power of inversify, we are only injecting dependencies like services, helpers, utils...
- You don't want to inject your dependencies as properties.
How register a dependency
Inversify needs an id to register our dependencies, this wrapper is going to do this for you 'magically' but if you want to uglify the code, keep reading the docs 🤓.
First of all create a class and an interface with the public methods of your class.
export interface IService1 {
method1(): string;
}
@injectable()
export class Service1 implements IService1 {
method1(): string {
return 'method 1';
}
}
Note: Don't forget to decorate the class as @injectable()
this will made your class candidate to be injectable inside other.
Now is time to register the service in the container, we usually do that in app.container.ts
or app.ts
.
container.addSingleton<IService1>(Service1);
How to test
There are some helper functions to test, the recommended way to test is beforeEach test:
- Reset the Container
- Register again all the dependencies of the container (this is your job)
- Mock all the necessary dependencies for the test
beforeEach(() => {
resetContainer();
containerBuilder();
mockSingleton<IHttpService>(cid.IHttpService, HttpServiceMock);
});
Other ways to register a class
As inversify accepts, we have configured three types of registration.
- Singleton: The dependency will be created only once, one dependency - one object.
- Transient: The dependency will be created each time is injected, one dependency - one object per injection.
- Request: Special case of singleton, more info in official docs.
How to use in your components
Once your dependencies are registered in the container, is simple as create a property with the name and the interface.
export default class extends Component {
@inject() service1: IService1;
}
Note: Part of the magic is that the name of the property has to be the name of the interface, this is how we don't need to add the id
.
Some examples
How to configure Uglify or Terser
If you want to use Uglify or Terser to obfuscate the code, you will need to add this options to preserve the names of the classes (we need them to generate the ids magically
😉).
new UglifyJSPlugin({
uglifyOptions: {
keep_classnames: true,
keep_fnames: true,
}
});
new TerserPlugin({
terserOptions: {
keep_classnames: true,
keep_fnames: true,
}
});
Next steps
- Investigate if can we remove
@injectable